#include "loops.h"
#include "list.h"
#include "calls.h"
#include "../../code/callfactory.h"
#include "../../semantics/visitordata.h"
#include "../../code/opcodes.h"

void FromLoopNode::write(GraphStream * s)
{
 this->accept(s);
 fprintf(s->stream,"\tnode_%d [label=\"(%d,%d)\\nLoop node\" shape=\"rectangle\"]\n", this->number(),this->line(),this->position());
 m_from->write(s);
 fprintf(s->stream,"\tnode_%d -> node_%d [label=\"from\"]\n",this->number(),m_from->number());
 m_cond->write(s);
 fprintf(s->stream,"\tnode_%d -> node_%d [label=\"condition\"]\n",this->number(),m_cond->number());
 m_loop->write(s);
 fprintf(s->stream,"\tnode_%d -> node_%d [label=\"body\"]\n",this->number(),m_loop->number());   
}

FromLoopNode::~FromLoopNode()
{
	if (this->isOwningData())
	{
		delete m_from;
		delete m_cond;
		delete m_loop;
	}
	for (unsigned int i=0;i<m_applied.size();i++)
		delete m_applied[i];
}

bool FromLoopNode::transformCode(ParentData * data)
{
	bool ok=true;
	if (m_from)
	{
	  Compound * from=static_cast<Compound *>(m_from);
	  for (unsigned int i=0;i<from->vector().size();i++)
	  {
			ok=ok && from->vector()[i]->transformCode(data);
	  }
	}
	if (m_loop)
	{
		Compound * loop=static_cast<Compound *>(m_loop);
		for (unsigned int i=0;i<loop->vector().size();i++)
		{
			AbstractNode * tmp=loop->vector()[i];
			bool tmpresult=tmp->transformCode(data);
			ok=ok && tmpresult;
		}
	}
	return ok;
}

bool FromLoopNode::checkLogic(ParentData * data)
{
	bool ok=true;
	if (m_from)
	{
	  Compound * from=static_cast<Compound *>(m_from);
	  for (unsigned int i=0;i<from->vector().size();i++)
	  {
			ok=ok && from->vector()[i]->checkLogic(data);
	  }
	}
	if (m_loop)
	{
		Compound * loop=static_cast<Compound *>(m_loop);
		for (unsigned int i=0;i<loop->vector().size();i++)
		{
			AbstractNode * tmp=loop->vector()[i];
			bool tmpresult=tmp->checkLogic(data);
			ok=ok && tmpresult;
		}
	}
	return ok;
}


void FromLoopNode::checkCommonErrors(ParentData * data)
{
	if (m_from)
	{
	  Compound * from=static_cast<Compound *>(m_from);
	  for (unsigned int i=0;i<from->vector().size();i++)
	  {
			AbstractNode * node=from->vector()[i];
			node->checkCommonErrors(data);
	  }
	}
	if (m_loop)
	{
		Compound * loop=static_cast<Compound *>(m_loop);
		for (unsigned int i=0;i<loop->vector().size();i++)
		{
			AbstractNode * tmp=loop->vector()[i];
			tmp->checkCommonErrors(data);
		}
	}
	m_cond->checkCommonErrors(data);

	//Perform cast to boolean if needed
	std::string condType=m_cond->getType(data);
	if (condType!="BOOLEAN")
	{
		//Select how we can convert to boolean and apply methods
		ClassSemantic * sem=data->Classes->getClassByName(condType);
		//If we don't get anything, error has appeared before
		if (sem)
		{
			bool isAncestor=data->Classes->isAncestor("BOOLEAN",condType);
			bool canConvert=sem->convertsTo("BOOLEAN");
			if (isAncestor && canConvert)
			{
				data->Classes->add(new ConvertConform(m_cond,condType,"BOOLEAN"));
			}
			else
			{
				if (isAncestor)
				{
					m_applied.push_back(new UpcastDelegate(sem,data->Classes->getClassByName("BOOLEAN")));
				}
				else
				{
					if (canConvert)
					{
						std::string name=sem->getConversionMethodTo("BOOLEAN");
						Method * meth=sem->getMethodByName(name);
						bool isPrecursor=false;
						if (m_cond->isIdentifier())
						{
							isPrecursor=static_cast<IdentifierNode*>(m_cond)->data()=="PRECURSOR";
						}
						bool isInterface=sem->isInterface();
						
						if (isInterface)
						{
							m_applied.push_back(new InvokeInterface(sem,meth,NULL));
						}
						if (isPrecursor && !isInterface)
						{
							m_applied.push_back(new InvokePrecursor(sem,meth,NULL));
						}
						if (!isPrecursor && !isInterface)
						{
							m_applied.push_back(new InvokeCommon(sem,meth,NULL));
						}
						//Check errors
						if (isPrecursor && ((meth->methodType() & FDT_ABSTRACT) !=0))
						{
							data->Classes->add(new AbstractPrecursorCall(m_cond,name,condType));
						}
						if (meth->isAvailable(data->CurrentClass->cnode()->name(),data->Classes)==false)
						{
							data->Classes->add(new MethodIsInaccessible(m_cond,name,data->CurrentClass->cnode()->name()));
						}
					}
					else data->Classes->add(new IncompatibleTypes(m_cond,condType,"BOOLEAN"));
				}
			}

		}
	}
	m_applied.push_back(new InvokeGetBoolean());
			
}


void FromLoopNode::fillConstant(ParentData * data)
{
	if (m_from)
	{
		m_from->fillConstant(data);
	}
	for (unsigned int i=0;i<m_applied.size();i++)
		m_applied[i]->fillConstant(data);
	if (m_cond)
	{
		m_cond->fillConstant(data);
	}

	if (m_loop)
	{
		m_loop->fillConstant(data);
	}
}

void FromLoopNode::produceCode(ParentData * data)
{
	if (m_from)
	{
		m_from->produceCode(data);
	}
	JBFile * file=data->currentFile();
	short condition_offset=file->getOffset();
	m_cond->produceCode(data);
	for (unsigned int i=0;i<m_applied.size();i++)
		m_applied[i]->produceCode(data);
	file->write(IFNE);
	short jump_to_end_offset=file->getOffset();
	file->reserveBytes(sizeof(short));
	if (m_loop)
	{
		m_loop->produceCode(data);
	}
	
	file->write(GOTO);
	short end_offset=file->getOffset()-1;
	file->write((short)(condition_offset-end_offset));
	file->writeAt((short)(end_offset+1+sizeof(short)-jump_to_end_offset+1),jump_to_end_offset);
	
}